package com.viknix.threadpool.manager.client.http;

import com.viknix.threadpool.manager.client.constant.Constants;
import com.viknix.threadpool.manager.client.constant.PropertyKeyConst;
import com.viknix.threadpool.manager.client.exception.TpmProcessException;
import com.viknix.threadpool.manager.client.util.ParamUtil;
import com.viknix.threadpool.manager.client.util.TemplateUtils;
import lombok.extern.slf4j.Slf4j;

import com.viknix.threadpool.manager.client.http.HttpSimpleClient.HttpResult;
import org.apache.commons.lang3.math.NumberUtils;

import java.io.IOException;
import java.net.ConnectException;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.util.List;
import java.util.Properties;
import java.util.concurrent.Callable;

/**
 * @Author: Dongqi
 * @Date: 2021/10/22 15:17
 * @Version 1.0
 * @Description: ServerHttpAgent
 */
@Slf4j
public class ServerHttpAgent implements HttpAgent {

    private String accessKey;
    private String secretKey;
    private String encode;
    private int maxRetry = 3;
    final ServerListManager serverListMgr;

    public ServerHttpAgent(ServerListManager mgr) {
        serverListMgr = mgr;
    }

    public ServerHttpAgent(ServerListManager mgr, Properties properties) {
        serverListMgr = mgr;
        init(properties);
    }

    public ServerHttpAgent(Properties properties) throws TpmProcessException {
        serverListMgr = new ServerListManager(properties);
        init(properties);
    }

    private void init(Properties properties) {
        initEncode(properties);
        initMaxRetry(properties);
    }

    private void initEncode(Properties properties) {
        // 默认 UTF-8
        encode = TemplateUtils.stringEmptyAndThenExecute(properties.getProperty(PropertyKeyConst.ENCODE), new Callable<String>() {
            @Override
            public String call() throws Exception {
                return Constants.ENCODE;
            }
        });
    }

    private void initMaxRetry(Properties properties) {
        // 默认是 3
        maxRetry = NumberUtils.toInt(String.valueOf(properties.get(PropertyKeyConst.MAX_RETRY)), Constants.MAX_RETRY);
    }

    /**
     * @param path          相对于web应用根，以/开头
     * @param headers
     * @param paramValues
     * @param encoding
     * @param readTimeoutMs
     * @return
     * @throws IOException
     */
    @Override
    public HttpResult httpGet(String path, List<String> headers, List<String> paramValues, String encoding,
                              long readTimeoutMs) throws IOException {
        final long endTime = System.currentTimeMillis() + readTimeoutMs;
        final boolean isSSL = false;

        String currentServerAddr = serverListMgr.getCurrentServerAddr();
        int maxRetry = this.maxRetry;

        do {
            try {
//                List<String> newHeaders = getSpasHeaders(paramValues);
//                if (headers != null) {
//                    newHeaders.addAll(headers);
//                }
                String url = getUrl(currentServerAddr, path);
                HttpResult result = HttpSimpleClient.httpGet(url, headers, paramValues, encoding, readTimeoutMs, isSSL);
                if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR
                        || result.code == HttpURLConnection.HTTP_BAD_GATEWAY
                        || result.code == HttpURLConnection.HTTP_UNAVAILABLE) {
                    log.error("[Tpm ConnectException] currentServerAddr: {}, httpCode: {}",
                            serverListMgr.getCurrentServerAddr(), result.code);
                } else {
                    // Update the currently available server addr
                    serverListMgr.updateCurrentServerAddr(currentServerAddr);
                    return result;
                }
            } catch (ConnectException ce) {
                log.error("[Tpm ConnectException httpGet] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServerAddr(), ce.getMessage());
            } catch (SocketTimeoutException stoe) {
                log.error("[Tpm SocketTimeoutException httpGet] currentServerAddr:{}， err : {}", serverListMgr.getCurrentServerAddr(), stoe.getMessage());
            } catch (IOException ioe) {
                log.error("[Tpm IOException httpGet] currentServerAddr: " + serverListMgr.getCurrentServerAddr(), ioe);
                throw ioe;
            }

            if (serverListMgr.getIterator().hasNext()) {
                currentServerAddr = serverListMgr.getIterator().next();
            } else {
                maxRetry--;
                if (maxRetry < 0) {
                    throw new ConnectException("[Tpm HTTP-GET] The maximum number of tolerable server reconnection errors has been reached");
                }
                serverListMgr.refreshCurrentServerAddr();
            }
        } while (System.currentTimeMillis() <= endTime);

        log.error("no available server");
        throw new ConnectException("no available server");
    }

    @Override
    public HttpResult httpPost(String path, List<String> headers, List<String> paramValues, String encoding,
                               long readTimeoutMs) throws IOException {
        final long endTime = System.currentTimeMillis() + readTimeoutMs;
        boolean isSSL = false;

        String currentServerAddr = serverListMgr.getCurrentServerAddr();
        int maxRetry = this.maxRetry;

        do {

            try {
//                List<String> newHeaders = getSpasHeaders(paramValues);
//                if (headers != null) {
//                    newHeaders.addAll(headers);
//                }
                String url = getUrl(currentServerAddr, path);
                HttpResult result = HttpSimpleClient.httpPost(url, headers, paramValues, encoding, readTimeoutMs, isSSL);
                if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR
                        || result.code == HttpURLConnection.HTTP_BAD_GATEWAY
                        || result.code == HttpURLConnection.HTTP_UNAVAILABLE) {
                    log.error("[Tpm ConnectException] currentServerAddr: {}, httpCode: {}",
                            currentServerAddr, result.code);
                } else {
                    // Update the currently available server addr
                    serverListMgr.updateCurrentServerAddr(currentServerAddr);
                    return result;
                }
            } catch (ConnectException ce) {
                log.error("[Tpm ConnectException httpPost] currentServerAddr: {}, err : {}", currentServerAddr, ce.getMessage());
            } catch (SocketTimeoutException stoe) {
                log.error("[Tpm SocketTimeoutException httpPost] currentServerAddr: {}， err : {}", currentServerAddr, stoe.getMessage());
            } catch (IOException ioe) {
                log.error("[Tpm IOException httpPost] currentServerAddr: " + currentServerAddr, ioe);
                throw ioe;
            }

            if (serverListMgr.getIterator().hasNext()) {
                currentServerAddr = serverListMgr.getIterator().next();
            } else {
                maxRetry--;
                if (maxRetry < 0) {
                    throw new ConnectException("[Tpm HTTP-POST] The maximum number of tolerable server reconnection errors has been reached");
                }
                serverListMgr.refreshCurrentServerAddr();
            }

        } while (System.currentTimeMillis() <= endTime);

        log.error("no available server, currentServerAddr : {}", currentServerAddr);
        throw new ConnectException("no available server, currentServerAddr : " + currentServerAddr);
    }

    @Override
    public HttpResult httpDelete(String path, List<String> headers, List<String> paramValues, String encoding,
                                 long readTimeoutMs) throws IOException {
        final long endTime = System.currentTimeMillis() + readTimeoutMs;
        boolean isSSL = false;

        String currentServerAddr = serverListMgr.getCurrentServerAddr();
        int maxRetry = this.maxRetry;

        do {
            try {
//                List<String> newHeaders = getSpasHeaders(paramValues);
//                if (headers != null) {
//                    newHeaders.addAll(headers);
//                }
                HttpResult result = HttpSimpleClient.httpDelete(
                        getUrl(currentServerAddr, path), headers, paramValues, encoding,
                        readTimeoutMs, isSSL);
                if (result.code == HttpURLConnection.HTTP_INTERNAL_ERROR
                        || result.code == HttpURLConnection.HTTP_BAD_GATEWAY
                        || result.code == HttpURLConnection.HTTP_UNAVAILABLE) {
                    log.error("[TPM ConnectException] currentServerAddr: {}, httpCode: {}",
                            serverListMgr.getCurrentServerAddr(), result.code);
                } else {
                    // Update the currently available server addr
                    serverListMgr.updateCurrentServerAddr(currentServerAddr);
                    return result;
                }
            } catch (ConnectException ce) {
                log.error("[TPM ConnectException httpDelete] currentServerAddr:{}, err : {}", serverListMgr.getCurrentServerAddr(), ce.getMessage());
            } catch (SocketTimeoutException stoe) {
                log.error("[TPM SocketTimeoutException httpDelete] currentServerAddr:{}， err : {}", serverListMgr.getCurrentServerAddr(), stoe.getMessage());
            } catch (IOException ioe) {
                log.error("[TPM IOException httpDelete] currentServerAddr: " + serverListMgr.getCurrentServerAddr(), ioe);
                throw ioe;
            }

            if (serverListMgr.getIterator().hasNext()) {
                currentServerAddr = serverListMgr.getIterator().next();
            } else {
                maxRetry--;
                if (maxRetry < 0) {
                    throw new ConnectException("[TPM HTTP-DELETE] The maximum number of tolerable server reconnection errors has been reached");
                }
                serverListMgr.refreshCurrentServerAddr();
            }

        } while (System.currentTimeMillis() <= endTime);

        log.error("no available server");
        throw new ConnectException("no available server");
    }

    private String getUrl(String serverAddr, String relativePath) {
        return serverAddr + "/" + serverListMgr.getContentPath() + relativePath;
    }

    public static String getAppname() {
        return ParamUtil.getAppName();
    }

    @Override
    public String getName() {
        return serverListMgr.getName();
    }

    @Override
    public String getNamespace() {
        return serverListMgr.getNamespace();
    }

    @Override
    public String getEncode() {
        return encode;
    }

    @Override
    public synchronized void start() throws TpmProcessException {
        serverListMgr.start();
    }
}
